package jsr166.contrib.uncontended;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;

class ReadWriteLockedMap<K, V, M extends Map<K,V>> implements Map<K, V> {
   
    volatile Set<Map.Entry<K, V>> entrySet;

    volatile Set<K> keys;

    final M map;

    final Lock readLock, writeLock;

    final ReadWriteLock rwLock;

    volatile Collection<V> values;
        
    public ReadWriteLockedMap(M map, ReadWriteLock rwLock) {
        this.map = map;
        this.rwLock = rwLock;
        readLock = rwLock.readLock();
        writeLock = rwLock.writeLock();
    }

    public void clear() {
        writeLock.lock();
        try {
            map.clear();
        } finally {
            writeLock.unlock();
        }

    }

    public boolean containsKey(Object arg0) {
        readLock.lock();
        try {
            return map.containsKey(arg0);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public boolean equals(Object arg0) {
        if (!(arg0 instanceof Map)) return false;
        Map<?,?> m = (Map)arg0;
        readLock.lock();
        try {
            if (size() != m.size()) return false;
            for(Map.Entry<K, V> e : entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                if (value == null) {
                    if (!(m.get(key)==null && m.containsKey(key)))
                        return false;
                } else {
                    if (!value.equals(m.get(key)))
                        return false;
                }

            }
            return true;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public int hashCode() {
        readLock.lock();
        try {
            int h = 0;
            for(Map.Entry<K, V> e : entrySet()) 
                h += e.hashCode();
            return h;
        } finally {
            readLock.unlock();
        }
    }
    public boolean containsValue(Object arg0) {
        readLock.lock();
        try {
            return map.containsValue(arg0);
        } finally {
            readLock.unlock();
        }
    }
    public Set<Map.Entry<K, V>> entrySet() {
        readLock.lock();
        try {
            if (entrySet == null)
                synchronized (this) {
                    if (entrySet == null)
                        entrySet = new ReadWriteLockedSet<Map.Entry<K, V>>(map.entrySet(), rwLock);
                }
            return entrySet;
        } finally {
            readLock.unlock();
        }
    }

    public V get(Object arg0) {
        readLock.lock();
        try {
            return map.get(arg0);
        } finally {
            readLock.unlock();
        }
    }

    public boolean isEmpty() {
        readLock.lock();
        try {
            return map.isEmpty();
        } finally {
            readLock.unlock();
        }
    }

    public Set<K> keySet() {
        readLock.lock();
        try {
            if (keys == null)
                synchronized (this) {
                    if (keys == null)
                        keys = new ReadWriteLockedSet<K>(map.keySet(), rwLock);

                }
            return keys;
        } finally {
            readLock.unlock();
        }
    }

    public V put(K arg0, V arg1) {
        writeLock.lock();
        try {
            return map.put(arg0, arg1);
        } finally {
            writeLock.unlock();
        }
    }

    public void putAll(Map<? extends K, ? extends V> arg0) {
        writeLock.lock();
        try {
            map.putAll(arg0);
        } finally {
            writeLock.unlock();
        }

    }

    public V remove(Object arg0) {
        writeLock.lock();
        try {
            return map.remove(arg0);
        } finally {
            writeLock.unlock();
        }
    }

    public int size() {
        readLock.lock();
        try {
            return map.size();
        } finally {
            readLock.unlock();
        }
    }

    public Collection<V> values() {
        readLock.lock();
        try {
            if (values == null)
                synchronized (this) {
                    if (values == null)
                        values = new ReadWriteLockedCollection<V, Collection<V>>(map.values(), rwLock);
                }
            return values;
        } finally {
            readLock.unlock();
        }
    }

}